package com.xinz.dao;

import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.code.morphia.Datastore;
import com.google.code.morphia.Morphia;
import com.mongodb.DBAddress;
import com.mongodb.Mongo;
import com.mongodb.MongoException;
import com.mongodb.MongoOptions;
import com.xinz.Constants;
import com.xinz.config.GlobalConfig;

/**
 * db管理
 * 分区采用master-slave结构，master为1，slave为n
 *
 */
public class DBManager {

	private static final Logger log = LoggerFactory.getLogger(DBManager.class);
	
	private static MongoDb master= null;
	private static List<MongoDb> slaves = new ArrayList<MongoDb>();
	
	private static final Morphia morphia = new Morphia();
	
	private static Random rand = new Random();
	
	private static final String DB_MASTER = "master";
	private static final String DB_SLAVE = "slave";
	
	/**
	 * DB类
	 *
	 */
	public static class MongoDb {
		private String dbName;
		private Mongo mongo;
		
		public MongoDb(String dbName, Mongo mongo) {
			this.dbName = dbName;
			this.mongo = mongo;
		}

		public String getDbName() {
			return dbName;
		}

		public Mongo getMongo() {
			return mongo;
		}
	}
	
	public static void initMongo() throws Exception {
		Element serverElement = GlobalConfig.getServer(Constants.SERVER_ID);
		
		// 初始化Master库
		Element masterElement = serverElement.element(DB_MASTER);
		String masterHost = masterElement.attributeValue("host");
		String masterDbName = masterElement.attributeValue("dbname");
		int masterPort = Integer.parseInt(masterElement.attributeValue("port"));
		
		Mongo masterMongo = createMongo(masterHost, masterPort, masterDbName);
		if(masterMongo == null) {
			throw new Exception("init db master exception");
		}
		master = new MongoDb(masterDbName, masterMongo);
		
		// 初始化Slave库，Slave以后可能会出现多个（spot_0_s0）
		List<Element> slaveElements = masterElement.elements(DB_SLAVE);
		for(Element slaveElement : slaveElements) {
			String slaveHost = slaveElement.attributeValue("host");
			String slaveDbName = slaveElement.attributeValue("dbname");
			int slavePort = Integer.parseInt(slaveElement.attributeValue("port"));
			Mongo slaveMongo = createMongo(slaveHost, slavePort, slaveDbName);
			if(slaveMongo == null) {
				throw new Exception("init slave db exception");
			}
			MongoDb slave = new MongoDb(slaveDbName, slaveMongo);
			slaves.add(slave);
		}
	}
	
	/**
	 * 创建Mongo实例
	 * @param host
	 * @param port
	 * @param dbName
	 * @return
	 */
	private static Mongo createMongo(String host, int port, String dbName) {
		try {
			MongoOptions options = new MongoOptions();
			options.autoConnectRetry = true;
			// options.threadsAllowedToBlockForConnectionMultiplier = 3;
			// options.connectionsPerHost = 2;
			Mongo mongo = new Mongo(new DBAddress(host, port, dbName), options);
			log.debug("new mongo:{}|{}", mongo.hashCode(), mongo.debugString());
			return mongo;
		}
		catch(UnknownHostException ex) {
			log.error("UnknownHostException", ex);
		}
		catch(MongoException ex) {
			log.error("MongoException:{}|{}", ex.hashCode(), ex.getMessage());
		}
		return null;
	}
	
	/**
	 * 获取Mongo实例
	 * @param type
	 * @return
	 */
	public static MongoDb getMongo(String type) {
		if(type == DB_MASTER) {
			return master;
		}
		else {
			// 随即给一个Slave
			int index = rand.nextInt(slaves.size());
			return slaves.get(index);
		}
	}
	
	/**
	 * 获取Datastore
	 * @return
	 */
	public static Datastore getDatastore() {
		return getDatastore(DB_MASTER);
	}
	
	/**
	 * 获取Datastore
	 * @param type
	 * @return
	 */
	public static Datastore getDatastore(String type) {
		MongoDb mongoDb = getMongo(type);
		String dbName = mongoDb.getDbName();
		Mongo mongo = mongoDb.getMongo();
		return morphia.createDatastore(mongo, dbName);
	}
}
